using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.IO;
using System.Xml.Serialization;

namespace Workflows.Components.Reports
{
    public class RdlGenerator : IRdlGenerator
    {
        Dictionary<int, ReportItemBase> _ReportItems;
        List<QueryParameterItem> _QueryParams;
        //private List<string> _AllFields;
        //private List<string> _SelectedFields;
        //private string _TableName;

        public RdlGenerator()
        {
            this._ReportItems = new Dictionary<int, ReportItemBase>();
            this._QueryParams = new List<QueryParameterItem>();
        }

        #region props
        public Dictionary<int, ReportItemBase> ReportItems
        {
            get
            {
                return this._ReportItems;
            }
            set
            {
                this._ReportItems = value;
            }
        }

        public List<QueryParameterItem> QueryParams
        {
            get
            {
                return this._QueryParams;
            }
            set
            {
                this._QueryParams = value;
            }
        }
        #endregion

        #region RDL -- body
        private Report CreateReport()
        {
            Report report = new Workflows.Components.Reports.Report();
            report.Items = new object[]{
                this.CreateDataSources(),
                this.CreateBody(),
                this.CreateDataSets(),
                "6.15in", 
                "1in", 
                "1in", 
                "1in", 
                "1in"};
            report.ItemsElementName = new Workflows.Components.Reports.ItemsChoiceType37[]{
                ItemsChoiceType37.DataSources,
                ItemsChoiceType37.Body,
                ItemsChoiceType37.DataSets,
                ItemsChoiceType37.Width,
                ItemsChoiceType37.LeftMargin,
                ItemsChoiceType37.TopMargin,
                ItemsChoiceType37.RightMargin,
                ItemsChoiceType37.BottomMargin};
            return report;
        }

        private DataSourcesType CreateDataSources()
        {
            DataSourcesType dataSources = new Workflows.Components.Reports.DataSourcesType();
            dataSources.DataSource = new Workflows.Components.Reports.DataSourceType[] { this.CreateDataSource() };
            return dataSources;
        }

        private DataSourceType CreateDataSource()
        {
            DataSourceType dataSource = new Workflows.Components.Reports.DataSourceType();
            dataSource.Name = "DummyDataSource";
            dataSource.Items = new object[] { this.CreateConnectionProperties() };
            return dataSource;
        }

        private ConnectionPropertiesType CreateConnectionProperties()
        {
            ConnectionPropertiesType connectionProperties = new Workflows.Components.Reports.ConnectionPropertiesType();
            connectionProperties.Items = new object[]{
                "",
                "SQL"};
            connectionProperties.ItemsElementName = new Workflows.Components.Reports.ItemsChoiceType[]{
                ItemsChoiceType.ConnectString,
                ItemsChoiceType.DataProvider};
            return connectionProperties;
        }

        private ReportParametersType CreateReportParameters()
        {
            ReportParametersType reportParameters = new ReportParametersType();
            if (this._QueryParams != null && this._QueryParams.Count > 0)
            {
                reportParameters.ReportParameter = new ReportParameterType[this._QueryParams.Count];
                for (int i = 0; i < this._QueryParams.Count; i++)
                {
                    reportParameters.ReportParameter[i] = this.CreateReportParameter(this._QueryParams[i]);
                }
            }
            return reportParameters;
        }

        private ReportParameterType CreateReportParameter(QueryParameterItem queryItem)
        {
            ReportParameterType reportParameter = new ReportParameterType();
            reportParameter.Name = queryItem.Name;
            reportParameter.Items = new object[]{
                this.GetQueryItemDataType(queryItem),
                false,
                true,
                "Prompt"};
            reportParameter.ItemsElementName = new ItemsChoiceType33[]{
                ItemsChoiceType33.DataType,
                ItemsChoiceType33.Nullable, 
                ItemsChoiceType33.AllowBlank, 
                ItemsChoiceType33.Prompt};
            return reportParameter;
        }

        private ReportParameterTypeDataType GetQueryItemDataType(QueryParameterItem queryItem)
        {
            switch (queryItem.DataType)
            {
                case AggregateDataType.Bit:
                    return ReportParameterTypeDataType.Boolean;
                case AggregateDataType.Number:
                    return ReportParameterTypeDataType.Float;
                case AggregateDataType.String:
                    return ReportParameterTypeDataType.String;
                case AggregateDataType.Time:
                    return ReportParameterTypeDataType.DateTime;
                default:
                    return ReportParameterTypeDataType.String;
            }
        }

        private DefaultValueType CreateReportParameterDefaultValue(QueryParameterItem queryItem)
        {
            DefaultValueType defaultValue = new DefaultValueType();
            defaultValue.Items = new object[] { 
                this.CreateReportParameterDataSetReference(queryItem)};
            
            return defaultValue;
        }

        private DataSetReferenceType CreateReportParameterDataSetReference(QueryParameterItem queryItem)
        {
            DataSetReferenceType dataSetRef = new DataSetReferenceType();
            dataSetRef.Items = new object[]{
                "DataSet_1", 
                queryItem.Name};
            dataSetRef.ItemsElementName = new ItemsChoiceType31[]{
                ItemsChoiceType31.DataSetName, 
                ItemsChoiceType31.ValueField};
            return dataSetRef;
        }

        private ValidValuesType CreateReportParameterValidValues(QueryParameterItem queryItem)
        {
            ValidValuesType validValues = new ValidValuesType();
            validValues.Items = new object[]{
                this.CreateReportParameterValidValuesDataSetReference(queryItem)};
            return validValues;
        }

        private DataSetReferenceType CreateReportParameterValidValuesDataSetReference(QueryParameterItem queryItem)
        {
            DataSetReferenceType dataSetRef = new DataSetReferenceType();
            dataSetRef.Items = new object[]{
                "DataSet_1", 
                queryItem.Name};
            dataSetRef.ItemsElementName = new ItemsChoiceType31[]{
                ItemsChoiceType31.DataSetName, 
                ItemsChoiceType31.ValueField};
            return dataSetRef;
        }

        private BodyType CreateBody()
        {
            BodyType body = new Workflows.Components.Reports.BodyType();
            body.Items = new object[]{
                this.CreateReportItems(),
                "4.5in"};
            body.ItemsElementName = new Workflows.Components.Reports.ItemsChoiceType30[]{
                ItemsChoiceType30.ReportItems,
                ItemsChoiceType30.Height};
            return body;
        }

        private ReportItemsType CreateReportItems()
        {
            ReportItemsType reportItems = new ReportItemsType();
            
            if (this._ReportItems.Count > 0)
            {
                reportItems.Items = new object[this._ReportItems.Count];

                ReportItemBase[] rdlReportItems = new ReportItemBase[this._ReportItems.Count];
                this._ReportItems.Values.CopyTo(rdlReportItems, 0);
                for (int i = 0; i < rdlReportItems.Length; i++)
                {
                    switch (rdlReportItems[i].ItemType)
                    {
                        case RdlReportItemType.Table:
                            TableReportItem tableReportItem = rdlReportItems[i] as TableReportItem;
                            TableRdlGenerator tableGen = new TableRdlGenerator(tableReportItem.Name);
                            tableGen.Fields = tableReportItem.ColumnNames;
                            reportItems.Items[i] = tableGen.CreateTable();
                            break;
                        case RdlReportItemType.Chart:
                            ChartReportItem chartItem = rdlReportItems[i] as ChartReportItem;
                            ColumnChartRdlGenerator chartGen = new ColumnChartRdlGenerator();
                            chartGen.ChartItem = chartItem;
                            ChartType chart = chartGen.GenerateColumnChart();
                            reportItems.Items[i] = chart;
                            break;
                        default:
                            break;
                    }
                }
            }

            return reportItems;
        }

        private DataSetsType CreateDataSets()
        {
            DataSetsType dataSets = new DataSetsType();
            List<DataSetType> dataSetList = new List<DataSetType>();
            if (this._ReportItems.Count > 0)
            {
                ReportItemBase[] rdlReportItems = new ReportItemBase[this._ReportItems.Count];
                this._ReportItems.Values.CopyTo(rdlReportItems, 0);
                for (int i = 0; i < rdlReportItems.Length; i++)
                {
                    switch (rdlReportItems[i].ItemType)
                    {
                        case RdlReportItemType.Table:
                            TableReportItem tableReportItem = rdlReportItems[i] as TableReportItem;
                            DataSetType dataSet1 = this.CreateDataSet(tableReportItem);
                            dataSetList.Add(dataSet1);
                            break;
                        case RdlReportItemType.Chart:
                            ChartReportItem chartItem = rdlReportItems[i] as ChartReportItem;
                            DataSetType dataSet2 = this.CreateDataSet(chartItem);
                            dataSetList.Add(dataSet2);
                            break;
                        default:
                            break;
                    }
                }
                dataSets.DataSet = dataSetList.ToArray();
            }
            return dataSets;
        }

        private DataSetType CreateDataSet(TableReportItem tableReportItem)
        {
            DataSetType dataSet = new DataSetType();
            dataSet.Name = "DataSet_" + tableReportItem.Id.ToString();
            dataSet.Items = new object[]{
                this.CreateQuery(), 
                this.CreateFields(tableReportItem)};
            return dataSet;
        }

        private DataSetType CreateDataSet(ChartReportItem chartItem)
        {
            DataSetType dataSet = new DataSetType();
            dataSet.Name = "DataSet_" + chartItem.Id.ToString();
            dataSet.Items = new object[]{
                this.CreateQuery(),
                this.CreateFields(chartItem)};
            return dataSet;
        }

        private QueryType CreateQuery()
        {
            QueryType query = new QueryType();
            if (this._QueryParams != null && this._QueryParams.Count > 0)
            {
                query.Items = new object[]{
                "DummyDataSource",
                "", 
                this.CreateQueryParameters()};
                query.ItemsElementName = new ItemsChoiceType2[]{
                ItemsChoiceType2.DataSourceName,
                ItemsChoiceType2.CommandText, 
                ItemsChoiceType2.QueryParameters};
            }
            else
            {
                query.Items = new object[]{
                "DummyDataSource",
                ""};
                query.ItemsElementName = new ItemsChoiceType2[]{
                ItemsChoiceType2.DataSourceName,
                ItemsChoiceType2.CommandText};
            }
            return query;
        }

        private QueryParametersType CreateQueryParameters()
        {
            QueryParametersType queryParameters = new QueryParametersType();
            if (this._QueryParams.Count > 0)
            {
                queryParameters.QueryParameter = new QueryParameterType[this._QueryParams.Count];
                for(int i=0;i<this._QueryParams.Count;i++)
                {
                    queryParameters.QueryParameter[i]=this.CreateQueryParameter(this._QueryParams[i]);
                }
            }
            return queryParameters;
        }

        private QueryParameterType CreateQueryParameter(QueryParameterItem queryItem)
        {
            QueryParameterType queryParam = new QueryParameterType();
            queryParam.Name = queryItem.Name;
            queryParam.Items = new object[]{
                queryItem.Value };
            return queryParam;
        }

        private FieldsType CreateFields(TableReportItem tableReportItem)
        {
            FieldsType fields = new FieldsType();
            fields.Field = new FieldType[tableReportItem.ColumnNames.Count];
            for (int i = 0; i < tableReportItem.ColumnNames.Count; i++)
            {
                fields.Field[i] = this.CreateField(tableReportItem.ColumnNames[i]);
            }
            return fields;
        }

        private FieldsType CreateFields(ChartReportItem chartItem)
        {
            FieldsType fields = new FieldsType();
            fields.Field = new FieldType[chartItem.ChartData.Categories.Count + 1];
            fields.Field[0] = this.CreateField("Count");
            for (int i = 0; i < chartItem.ChartData.Categories.Count; i++)
            {
                fields.Field[i + 1] = this.CreateField(chartItem.ChartData.Categories[i].Name);
            }
            
            return fields;
        }

        private FieldType CreateField(string fieldName)
        {
            FieldType field = new FieldType();
            field.Name = fieldName;
            field.Items = new object[] { fieldName };
            field.ItemsElementName = new ItemsChoiceType1[] { ItemsChoiceType1.DataField };
            return field;
        }
        #endregion

        private void WriteXml(Stream stream)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(Report));
            serializer.Serialize(stream, this.CreateReport());
        }

        #region IRdlGenerator Members

        public void AddTableReportItem(TableReportItem tableItem)
        {
            int lastIndex = this.GetLastReportItemIndex();
            tableItem.Id = lastIndex + 1;
            this._ReportItems.Add(tableItem.Id, tableItem);
        }

        public void AddMatrixReportItem(MatrixReportItem matrixItem)
        {
            int lastIndex = this.GetLastReportItemIndex();
            matrixItem.Id = lastIndex + 1;
            this._ReportItems.Add(matrixItem.Id, matrixItem);
        }

        public void AddChartReportItem(ChartReportItem chartItem)
        {
            int lastIndex = this.GetLastReportItemIndex();
            chartItem.Id = lastIndex + 1;
            this._ReportItems.Add(chartItem.Id, chartItem);
        }

        public void AddQueryParameterItem(QueryParameterItem queryItem)
        {
            this._QueryParams.Add(queryItem);
        }

        public void GenerateRdlDocument(Stream stream)
        {
            this.WriteXml(stream);
        }
        #endregion


        #region util
        private int GetLastReportItemIndex()
        {
            int lastIndex = 0;
            if (this._ReportItems.Count > 0)
            {
                int[] indexes = new int[this._ReportItems.Count];
                this._ReportItems.Keys.CopyTo(indexes, 0);
                for (int i = 0; i < indexes.Length; i++)
                {
                    if (indexes[i] > lastIndex)
                    {
                        lastIndex = indexes[i];
                    }
                }
            }
            return lastIndex;
        }
        #endregion
    }
}
